﻿using System;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Windows.Forms;

namespace WindowsFormsGUI
{
    public partial class PuertoSerieFrm : Form
    {
        #region Atributos

        private SerialPort _puertoSerie;
        private bool _error;

        #endregion

        #region Constructor

        public PuertoSerieFrm()
        {
            InitializeComponent();

            _puertoSerie = new SerialPort();

            ObtenerPuetos();
            HabilitarControles(false);

            // Cuando el dato es recibido por el puerto, se llama a este metodo
            _puertoSerie.DataReceived += new SerialDataReceivedEventHandler(RecibirDatos);
        }

        #endregion

        #region Eventos de usuario

        private void BtnAbrirPuertoClick(object sender, EventArgs e)
        {
            if (_puertoSerie.IsOpen)
            {
                _puertoSerie.Close();

                lblPuerto.Text = _puertoSerie.PortName + " CERRADO";
                lblPuerto.BackColor = Color.Red;
            }
            else
            {
                _puertoSerie.PortName = cmbPuerto.Text;

                try
                {
                    _puertoSerie.Open();

                    tbxDatos.Focus();
                    tbxConsola.Clear();

                    lblPuerto.Text = _puertoSerie.PortName + " ABIERTO";
                    lblPuerto.BackColor = Color.Green;

                    tbxConsola.AppendText("Puerto abierto a las " + DateTime.Now + "\n");
                    tbxConsola.AppendText("BaudRate: " + _puertoSerie.BaudRate + "\n");
                    tbxConsola.AppendText("DataBits: " + _puertoSerie.DataBits + "\n");
                    tbxConsola.AppendText("StopBits: " + _puertoSerie.StopBits + "\n");
                    tbxConsola.AppendText("Parity: " + _puertoSerie.Parity + "\n");
                }
                catch (UnauthorizedAccessException ex)
                {
                    _error = true;
                    tbxConsola.AppendText("\n" + ex.Message);
                }
                catch (IOException ex)
                {
                    _error = true;
                    tbxConsola.AppendText("\n" + ex.Message);
                }
                catch (ArgumentException ex)
                {
                    _error = true;
                    tbxConsola.AppendText("\n" + ex.Message);
                }

                if (_error)
                {
                    MessageBox.Show(this, "No se pudo abrir el puerto COM. Lo más probable es que ya está en uso, ha sido eliminado o no está disponible.", "Puerto COM no disponible", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                }
            }

            HabilitarControles(true);
        }

        private void BtnCerrarPuertoClick(object sender, EventArgs e)
        {
            try
            {
                if (_puertoSerie.IsOpen)
                {
                    _puertoSerie.Close();

                    lblPuerto.Text = _puertoSerie.PortName + " CERRADO";
                    lblPuerto.BackColor = Color.Red;

                    tbxConsola.AppendText("Puerto cerrado. \n");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, "No se pudo cerrar el puerto COM. Lo más probable es que ya está en uso, ha sido eliminado o no está disponible.", "Puerto COM no disponible", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
        }

        private void BtnLimpiarClick(object sender, EventArgs e)
        {
            try
            {
                tbxDatos.Text = string.Empty;
                tbxConsola.Text = string.Empty;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
            }
        }

        private void BtnEnviarClick(object sender, EventArgs e)
        {
            try
            {
                tbxConsola.AppendText(">>" + tbxDatos.Text);

                byte[] data = HexStringToByteArray(tbxDatos.Text);
                // Envia los datos binarios al puerto
                _puertoSerie.Write(data, 0, data.Length);

                tbxDatos.Clear();
            }
            catch (FormatException ex)
            {
                // Informar al usuario si la cadena hexadecimal no se ha formateado correctamente
                tbxConsola.AppendText("No es el formato correcto cadena hexadecimal: " + tbxDatos.Text + "\n");
            }
        }

        private void PuertoSerieFrmLoad(object sender, EventArgs e)
        {
            try
            {
                //Desabilitamos la detección de llamadas a los miembros del proceso principal
                CheckForIllegalCrossThreadCalls = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
            }
        }

        #endregion

        #region Metodos de soporte

        private void ObtenerPuetos()
        {
            var listaPuertosSerie = SerialPort.GetPortNames().ToList();
            cmbPuerto.DataSource = listaPuertosSerie;
        }

        private void HabilitarControles(bool estado)
        {
            tbxDatos.Enabled = estado;
            btnEnviar.Enabled = estado;
        }

        private byte[] HexStringToByteArray(string buffer)
        {
            char[] a;
            int i;
            string temp = string.Empty;

            a = buffer.ToCharArray();

            for (i = 0; i < buffer.Length; i++)
            {
                if (a[i] > 1000)
                    continue;

                temp = temp + Uri.HexEscape(a[i]);
            }

            var hex = temp.Replace("%", "");

            return StringToByteArray(hex);
        }

        private byte[] StringToByteArray(string hex)
        {
            return Enumerable.Range(0, hex.Length)
                             .Where(x => x % 2 == 0)
                             .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                             .ToArray();
        }

        private void RecibirDatos(object sender, SerialDataReceivedEventArgs e)
        {
            if (_puertoSerie.IsOpen)
            {
                // Leer todos los datos que esperan en el buffer
                string data = _puertoSerie.ReadExisting();
                //Muestra el texto al usuario en el terminal
                tbxConsola.AppendText(data);
                tbxConsola.AppendText("\n");
            }
        }

        #endregion
    }
}